"""
This is minimarkup.py - a Python module that attempts to
make it easier to generate HTML/XML from a Python program
in an intuitive, lightweight, customizable and pythonic way.

The code is in the public domain, it comes with no warranty
and you can do absolutely whatever you want with it.

Documentation and further info can be found at:
https://code.google.com/p/python-practice-and-experience/wiki/MiniMarkup

Please send bug reports, feature requests, enhancement
ideas or questions to swc dot steve449 at gmail dot com.

Installation: drop minimarkup.py somewhere into your Python path.

This code is inspired by one of my favorite python packages: markup.py.
The real markup.py is at http://markup.sourceforge.net/
Thanks to nogradi at gmail dot com for the idea!
"""

def _render(kwd): # render key, value pairs
    kbits=[]
    for k,v in kwd.items():
        if k.endswith('_'): # trailing underscore has special meaning eg class_
            k = k[:-1]
        elif '_' in k: # underscore has special meaning eg http_equiv as http-equiv
            k = k.replace('_','-')
        if isinstance(v, bool): # bool type has special meaning eg selected (vo value)
            if v:
                kbits.append(k)
        else:
            kbits.append('%s="%s"' % (k,v))
    return ' %s' % ' '.join(kbits)

def _iter(arg, kwd):
    # normal (optimum case) no lists
    if type(arg)!=list:
        yield arg, kwd
        return
    # iterate over all arguments as lists
    maxlen=len(arg)
    for k,v in kwd.items():
        if type(v)!=list:
            kwd[k]=[v]
        maxlen= max(maxlen, len(kwd[k]))
    for idx in range(maxlen):
        a_out = arg[idx] if idx<len(arg) else arg[-1]
        k_out = dict()
        for k,v in kwd.items():
            k_out[k] = v[idx] if idx<len(v) else v[-1]
        yield a_out, k_out

class E0: # turn tags and attributes into strings

    def __init__(self, tag):
        self._tag = tag

    def _render(self, arg, kwd):
        bits = ['<%s' % self._tag]
        if kwd:
            bits.append(_render(kwd))
        bits.append('>')
        if arg is None: # special meaning add a close
            bits.append('</%s>' % self._tag)
        else:
            arg = str(arg)
            if arg:
                bits.append('%s</%s>' % (arg, self._tag))
        return  ''.join(bits)

    def __call__(self, arg='', **kwd): # construct the tag and fields
        return ''.join([self._render(a,k) for (a,k) in _iter(arg, kwd)])

    def close(self):
        return '</%s>' % self._tag

class E(E0): # handle the (normal) case when the page stores the data

    def __init__(self, tag, page):
        E0.__init__(self, tag)
        self._page = page

    def __call__(self, arg='', **kwd):
        self._page._add(self._tag, E0.__call__(self, arg, **kwd))
        return self # allow chaining of close methods

    def embedclose(self): # close the last open <tag ked="val"/>
        self._page._embedclose(self._tag)

    def noclose(self): # prevent any indent there is no close tag
        self._page._close()

    def close(self): # add a close tag
        result = E0.close(self)
        self._page._add(self._tag, result)
        return result

class _Q: # a one liner with no data store

    def __getattr__( self, attr ):
        return E0( attr )

Q=_Q() # use for one liners

class P0(object): # super fast and minimal no checking or indent or tags

    def __init__(self, type_=None):
        self._bits = []
        if type_:
            self += type_

    def _embedclose(self, tag):
        self._bits[-1] = '%s/>' % self._bits[-1][:-1]

    def _add(self, tag, bit):
        self._bits.append(bit)
        return self

    def __iadd__(self, bit):
        self._bits.append(bit)

    def __getattr__(self, tag):
        return E( tag, self )

    def __repr__(self):
        return ''.join(self._bits)
    __call__ = __repr__

class P(object): # indent with optional tag checking
    NOCLOSE = [] # override this for tags like meta and link and img
    TAGS = [] # override this to restrict the legal tag list

    def __init__(self, type_=None): # a new page with optional doc type
        self._bits = [] # the page data
        self._in = 0 # the indentation
        self._render = False # only render once
        if type_: # optional doc type e.g. html or xml
            self += type_

    def _close(self): # internal callback when there is no close tag
        assert not self._render
        self._in -= 1
        assert self._in >= 0, str(self) # ensure open and close tags are in pairs

    def _embedclose(self, tag): # internal callback to add embeded close tag
        assert not self._render
        old = self._bits[-1]
        assert old.strip().startswith('<%s'%tag) and old[-1]=='>', old
        self._bits[-1] = '%s/>' % old[:-1]
        self._close()

    def _add(self, tag, bit): # internal callback to add the tag to the page
        assert not self._render
        if bit.startswith('</'):
            self._close()
        self._bits.append(self._in*'\t'+bit)
        if tag not in self.NOCLOSE and '</' not in bit:
            self._in += 1
        return self

    def __iadd__(self, bit): # user API add any string to the page e.g. p += '...'
        assert not self._render
        self._bits.append(bit)
        return self

    def __getattr__( self, tag ): # user API add a tag to the page e.g. p.tag
        assert not self._render
        if self.TAGS:
            assert tag in self.TAGS, tag
        return E( tag, self )

    def __repr__(self): # user API str or repr or print e.g. str(p)
        return '\n'.join(self._bits)

    def __call__(self): # user API to render the output
        assert self._in == 0, str(self) # ensure the doc is correctly formed
        self._render = True
        return str(self)
